import { useCallback } from "react";
import { useState } from "react";
import { useMountRef } from "utils";

interface State<D> {
  data: D | null;
  error: Error | null;
  stat: "idle" | "loading" | "error" | "success";
}

const defaultInitialState: State<null> = {
  data: null,
  error: null,
  stat: "idle",
};

export const useAsync = <D>(initialState?: State<D>) => {
  const [state, setState] = useState({
    ...defaultInitialState,
    ...initialState,
  });

  const [retry, setRetry] = useState(() => () => {});
  const mountRef = useMountRef();

  const setData = useCallback((data: D) => {
    setState({
      data,
      error: null,
      stat: "success",
    });
  }, []);

  const setError = useCallback((error: Error | null) => {
    setState({
      error,
      data: null,
      stat: "error",
    });
  }, []);

  const run = useCallback(
    (promise: Promise<D>, fetchConfig?: () => Promise<D>) => {
      if (!promise || !promise.then) {
        throw new Error("请传入 promise 类型的参数");
      }
      setRetry(() => () => {
        // 这里不能直接传入promise，因为这里的promise只是请求方法处理返回的promise
        if (fetchConfig) {
          run(fetchConfig());
        }
      });
      setState((prevState) => ({
        ...prevState,
        stat: "loading",
      }));
      return promise
        .then((data) => {
          if (mountRef.current) setData(data);
          return data;
        })
        .catch((error) => {
          setError(error);
          return error;
        });
    },
    [mountRef, setData, setError]
  );

  // 钩子返回他的属性
  return {
    isIdle: state.stat === "idle",
    isLoading: state.stat === "loading",
    isError: state.stat === "error",
    isSuccess: state.stat === "loading",
    run,
    setError,
    setData,
    // retry被调用时执行run这样可以更新state，最后可以刷新页面
    retry,
    ...state,
  };
};
